/*
 * PlayListPanel.java
 *
 * Created on June 15, 2005, 10:06 PM
 */
package com.bff.mpd.musicjukebox.ui;

import com.bff.bjj.ui.component.BJJPanel;
import com.bff.mpd.musicjukebox.ui.components.PlaylistPopupMenu;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import com.bff.mpd.utils.Utils;
import com.bff.mpd.musicjukebox.lib.JukeboxProperties;
import com.bff.mpd.musicjukebox.lib.actions.PlaylistDownAction;
import com.bff.mpd.musicjukebox.lib.actions.PlaylistLoopAction;
import com.bff.mpd.musicjukebox.lib.actions.PlaylistUpAction;
import com.bff.mpd.musicjukebox.ui.components.PlaylistActionPanel;
import com.bff.mpd.musicjukebox.ui.components.PlaylistCellRenderer;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Action;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.bff.javampd.MPDDatabase;
import org.bff.javampd.MPDPlayer;
import org.bff.javampd.MPDPlaylist;
import org.bff.javampd.events.PlayerBasicChangeEvent;
import org.bff.javampd.events.PlayerBasicChangeListener;
import org.bff.javampd.events.PlaylistBasicChangeEvent;
import org.bff.javampd.events.PlaylistBasicChangeListener;
import org.bff.javampd.events.PlaylistChangeEvent;
import org.bff.javampd.events.PlaylistChangeListener;
import org.bff.javampd.events.TrackPositionChangeEvent;
import org.bff.javampd.events.TrackPositionChangeListener;
import org.bff.javampd.exception.*;
import org.bff.javampd.objects.MPDSong;

/**
 *
 * @author  Bill
 */
public class PlaylistPanel extends BJJPanel
        implements PlaylistChangeListener,
        PlaylistBasicChangeListener,
        PlayerBasicChangeListener,
        TrackPositionChangeListener {

    private MPDPlaylist playlist;
    private MPDPlayer player;
    private MPDDatabase database;
    private MPDSong currentSong;
    private DefaultListModel playlistSongsModel = new DefaultListModel();
    private Action playlistUpAction;
    private Action playlistDownAction;
    private Action playlistLoopAction;
    private PlaylistActionPanel actionPanel;
    private static final int END_SONG_DIFF = 5;
    private boolean reachedEnd;
    private PlaylistCellRenderer playlistRenderer;

    /** Creates new form PlayListPanel */
    public PlaylistPanel() {       
        setPlaylist(JukeboxProperties.getInstance().getPlaylist());
        setPlayer(JukeboxProperties.getInstance().getPlayer());
        setDatabase(JukeboxProperties.getInstance().getDatabase());

        makeActions();
        initComponents();

        actionPanel = new PlaylistActionPanel();

        playlistRenderer = new PlaylistCellRenderer();
        listPlaylist.setCellRenderer(playlistRenderer);
        
        listSongs();
        try {
            setCurrentSong(getPlaylist().getCurrentSong());
        } catch (MPDConnectionException ex) {
            Logger.getLogger(PlaylistPanel.class.getName()).log(Level.SEVERE, null, ex);
        } catch (MPDPlaylistException ex) {
            Logger.getLogger(PlaylistPanel.class.getName()).log(Level.SEVERE, null, ex);
        }

        MouseListener songlistPopupListener = new SonglistPopupListener();
        listPlaylist.addMouseListener(songlistPopupListener);

        

        JukeboxProperties.getInstance().getMPDMonitor().addPlaylistChangeListener(this);
        JukeboxProperties.getInstance().getMPDMonitor().addPlayerChangeListener(this);
        JukeboxProperties.getInstance().getMPDMonitor().addTrackPositionChangeListener(this);
        playlist.addPlaylistChangeListener(this);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanel3 = new BJJPanel();
        jScrollPane = new javax.swing.JScrollPane();
        listPlaylist = new com.bff.mpd.musicjukebox.ui.components.PlaylistSongList(getPlaylist(), playlistSongsModel);
        jLabel1 = new javax.swing.JLabel();
        jPanel1 = new BJJPanel();
        jLabel2 = new javax.swing.JLabel();
        btnSave = new javax.swing.JButton();
        btnPlaylistLoop = new javax.swing.JButton(playlistLoopAction);
        btnPlaylistLoop.setText(null);

        setPreferredSize(new java.awt.Dimension(258, 500));
        setLayout(new java.awt.BorderLayout());

        jPanel3.setLayout(new java.awt.BorderLayout());

        listSongs();
        listPlaylist.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        listPlaylist.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                listPlaylistMouseClicked(evt);
            }
        });
        listPlaylist.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
                listPlaylistValueChanged(evt);
            }
        });
        jScrollPane.setViewportView(listPlaylist);

        jPanel3.add(jScrollPane, java.awt.BorderLayout.CENTER);

        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel1.setText("Current Playlist");
        jPanel3.add(jLabel1, java.awt.BorderLayout.NORTH);

        jPanel1.setLayout(new java.awt.BorderLayout());

        jLabel2.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel2.setText("Drag and Drop to Sequence");
        jPanel1.add(jLabel2, java.awt.BorderLayout.CENTER);

        btnSave.setBackground(jPanel1.getBackground());
        btnSave.setIcon(new javax.swing.ImageIcon(getClass().getResource("/resources/mpd/save.png"))); // NOI18N
        btnSave.setBorder(null);
        btnSave.setBorderPainted(false);
        btnSave.setContentAreaFilled(false);
        btnSave.setMaximumSize(new java.awt.Dimension(20, 20));
        btnSave.setMinimumSize(new java.awt.Dimension(20, 20));
        btnSave.setPreferredSize(new java.awt.Dimension(20, 20));
        btnSave.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnSaveActionPerformed(evt);
            }
        });
        jPanel1.add(btnSave, java.awt.BorderLayout.EAST);

        btnPlaylistLoop.setBorder(null);
        btnPlaylistLoop.setBorderPainted(false);
        btnPlaylistLoop.setContentAreaFilled(false);
        btnPlaylistLoop.setMaximumSize(new java.awt.Dimension(20, 20));
        btnPlaylistLoop.setMinimumSize(new java.awt.Dimension(20, 20));
        btnPlaylistLoop.setPreferredSize(new java.awt.Dimension(20, 20));
        jPanel1.add(btnPlaylistLoop, java.awt.BorderLayout.WEST);

        jPanel3.add(jPanel1, java.awt.BorderLayout.SOUTH);

        add(jPanel3, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents

    private void listPlaylistMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_listPlaylistMouseClicked
        if (evt.getClickCount() == 2) {
            try {
                getPlayer().playId((MPDSong) listPlaylist.getSelectedValue());
            } catch (MPDConnectionException ex) {
                Logger.getLogger(PlaylistPanel.class.getName()).log(Level.SEVERE, null, ex);
            } catch (MPDPlayerException ex) {
                Logger.getLogger(PlaylistPanel.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            JList lstCust = (JList) evt.getSource();
            int currIndex = lstCust.locationToIndex(evt.getPoint());
            MPDSong currData = (MPDSong) lstCust.getSelectedValue();
            Rectangle itemBounds = lstCust.getCellBounds(currIndex, currIndex);
            Point currPoint = new Point(evt.getX(), evt.getY());
            Component readyComponent = ((PlaylistCellRenderer) lstCust.getCellRenderer()).getListCellRendererComponent(lstCust,
                    currData, currIndex, true, true);
            readyComponent.setSize(itemBounds.width, itemBounds.height);
            Component pressedComponent = null;
            while (readyComponent != pressedComponent) {
                if (pressedComponent != null) {
                    readyComponent = pressedComponent;
                }
                pressedComponent = readyComponent.getComponentAt((int) (currPoint.getX() - itemBounds.getX()),
                        (int) (currPoint.getY() - itemBounds.getY()));

                if (pressedComponent == null) {
                    return;
                }
                currPoint.setLocation(currPoint.getX() - itemBounds.getX(), currPoint.getY() - itemBounds.getY());
                itemBounds = pressedComponent.getBounds();
            }

            if (pressedComponent instanceof JButton) {
                ((JButton) pressedComponent).doClick();
                //System.out.println("Pressed JButton with text " + ((JButton) pressedComponent).getText() + " in row " + currIndex + " of JList");
            }
            getPlaylistRenderer().getListPanels().get(currData).eventReceived(evt);
        }
    }//GEN-LAST:event_listPlaylistMouseClicked

    private void listPlaylistValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_listPlaylistValueChanged
        if (getSelectedSong() != null) {
            getActionPanel().setSelectedSong(getSelectedSong());

            if (getSelectedSong().getAlbum() != null
                    && getSelectedSong().getArtist() != null) {
                getActionPanel().enableSelectedActions();

                getActionPanel().setSelectedArtist(getSelectedSong().getArtist());
                getActionPanel().setSelectedAlbum(getSelectedSong().getAlbum());
            } else {
                getActionPanel().disableSelectedActions();
            }
        }
    }//GEN-LAST:event_listPlaylistValueChanged

    private void btnSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSaveActionPerformed
        createPlaylist();
}//GEN-LAST:event_btnSaveActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnPlaylistLoop;
    private javax.swing.JButton btnSave;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JScrollPane jScrollPane;
    private javax.swing.JList listPlaylist;
    // End of variables declaration//GEN-END:variables

    private void createPlaylist() {
        String s = (String) JOptionPane.showInputDialog(
                this,
                Utils.getMessage("PLAYLIST_CREATE_MESSAGE"),
                Utils.getMessage("PLAYLIST_CREATE_TITLE"),
                JOptionPane.OK_CANCEL_OPTION);

        if ((s == null) || (s.length() < 1)) {
            JOptionPane.showMessageDialog(this,
                    Utils.getMessage("PLAYLIST_CREATE_ERROR_TEXT"),
                    Utils.getMessage("PLAYLIST_CREATE_ERROR_TITLE"),
                    JOptionPane.ERROR_MESSAGE);

        } else {
            try {
                if (getDatabase().listPlaylists().contains(s)) {
                    JOptionPane.showMessageDialog(this,
                            Utils.getMessage("PLAYLIST_CREATE_EXISTS_TEXT"),
                            Utils.getMessage("PLAYLIST_CREATE_ERROR_TITLE"),
                            JOptionPane.ERROR_MESSAGE);
                } else {
                    getPlaylist().savePlaylist(s);
                }
            } catch (MPDConnectionException ex) {
                Logger.getLogger(PlaylistActionPanel.class.getName()).log(Level.SEVERE, null, ex);
            } catch (MPDPlaylistException ex) {
                Logger.getLogger(PlaylistActionPanel.class.getName()).log(Level.SEVERE, null, ex);
            } catch (MPDDatabaseException ex) {
                Logger.getLogger(PlaylistActionPanel.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public MPDSong getSelectedSong() {
        return (MPDSong) listPlaylist.getSelectedValue();
    }

    public void makeToolbar(JMenuBar menuBar) {
        makeToolbar(menuBar, -1);
    }

    public void makeToolbar(JMenuBar menuBar, int index) {
        JMenu playMenu = new JMenu("Playlist");
        playMenu.add(new JMenuItem(playlistUpAction));
        playMenu.add(new JMenuItem(playlistDownAction));
        menuBar.add(playMenu, index);
    }

    @Override
    public void playlistChanged(PlaylistChangeEvent pce) {
        switch (pce.getId()) {
            case PlaylistChangeEvent.SONG_ADDED:
            case PlaylistChangeEvent.SONG_DELETED:
            case PlaylistChangeEvent.ALBUM_ADDED:
            case PlaylistChangeEvent.ARTIST_ADDED:
            case PlaylistChangeEvent.GENRE_ADDED:
            case PlaylistChangeEvent.YEAR_ADDED:
            case PlaylistChangeEvent.FILE_ADDED:
            case PlaylistChangeEvent.PLAYLIST_ADDED:
            case PlaylistChangeEvent.PLAYLIST_DELETED:
            case PlaylistChangeEvent.PLAYLIST_CLEARED:
            case PlaylistChangeEvent.MULTIPLE_SONGS_ADDED:
                listSongs();
                break;
            case (PlaylistChangeEvent.SONG_SELECTED):
                try {
                    setCurrentSong(getPlaylist().getCurrentSong());
                } catch (MPDConnectionException ex) {
                    Logger.getLogger(PlaylistPanel.class.getName()).log(Level.SEVERE, null, ex);
                } catch (MPDPlaylistException ex) {
                    Logger.getLogger(PlaylistPanel.class.getName()).log(Level.SEVERE, null, ex);
                }
        }

        //always repaint
        try {
            repaint();
            Utils.getParentComponent(this).repaint();
        } catch (NullPointerException npe) {
            //don't care
            //might get this at startup
        }
    }

    private void makeActions() {
        playlistLoopAction = new PlaylistLoopAction(getPlayer());
        playlistUpAction = new PlaylistUpAction(listPlaylist, getPlaylist());
        playlistDownAction = new PlaylistDownAction(listPlaylist, getPlaylist());
    }

    public void setSelectedSong(MPDSong song) {
        listPlaylist.setSelectedValue(song, true);
    }

    private void listSongs() {
        playlistSongsModel.clear();
        List songList = null;
        try {
            songList = new ArrayList(getPlaylist().getSongList());
        } catch (MPDException e) {
            e.printStackTrace();
        }
        for (Iterator iter = songList.iterator(); iter.hasNext();) {
            playlistSongsModel.addElement((MPDSong) iter.next());
        }
    }

    /**
     * @return the playlist
     */
    private MPDPlaylist getPlaylist() {
        return playlist;
    }

    /**
     * @param playlist the playlist to set
     */
    private void setPlaylist(MPDPlaylist playlist) {
        this.playlist = playlist;
    }

    /**
     * @return the player
     */
    private MPDPlayer getPlayer() {
        return player;
    }

    /**
     * @param player the player to set
     */
    private void setPlayer(MPDPlayer player) {
        this.player = player;
    }

    /**
     * @return the database
     */
    private MPDDatabase getDatabase() {
        return database;
    }

    /**
     * @param database the database to set
     */
    private void setDatabase(MPDDatabase database) {
        this.database = database;
    }

    /**
     * @return the currentSong
     */
    public MPDSong getCurrentSong() {
        return currentSong;
    }

    /**
     * @param currentSong the currentSong to set
     */
    public void setCurrentSong(MPDSong currentSong) {
        this.currentSong = currentSong;
        getPlaylistRenderer().setCurrentSong(currentSong);
        if (currentSong != null) {
            getActionPanel().setCurrentSong(currentSong);
            getActionPanel().setCurrentArtist(currentSong.getArtist());
            getActionPanel().setCurrentAlbum(currentSong.getAlbum());
        } 
    }

    @Override
    public void playlistBasicChange(PlaylistBasicChangeEvent pbce) {
        switch (pbce.getId()) {
            case PlaylistBasicChangeEvent.SONG_CHANGED:
                try {

                    if (getActionPanel().isRemoveSong() && isReachedEnd()) {
                        getPlaylist().removeSong(getCurrentSong());
                    }
                    setCurrentSong(getPlaylist().getCurrentSong());

                } catch (MPDConnectionException ex) {
                    Logger.getLogger(PlaylistPanel.class.getName()).log(Level.SEVERE, null, ex);
                } catch (MPDPlaylistException ex) {
                    Logger.getLogger(PlaylistPanel.class.getName()).log(Level.SEVERE, null, ex);
                }
                listPlaylist.repaint();
                break;
            default:
                break;
        }
    }

    @Override
    public void playerBasicChange(PlayerBasicChangeEvent event) {
        switch (event.getId()) {
            case PlayerBasicChangeEvent.PLAYER_STARTED:
                try {
                    setCurrentSong(getPlaylist().getCurrentSong());
                } catch (MPDConnectionException ex) {
                    Logger.getLogger(PlaylistPanel.class.getName()).log(Level.SEVERE, null, ex);
                } catch (MPDPlaylistException ex) {
                    Logger.getLogger(PlaylistPanel.class.getName()).log(Level.SEVERE, null, ex);
                }
                break;
        }
    }

    /**
     * @return the actionPanel
     */
    public PlaylistActionPanel getActionPanel() {
        return actionPanel;
    }

    private boolean isRemoveSong() {
        return actionPanel.isRemoveSong();
    }

    @Override
    public void trackPositionChanged(TrackPositionChangeEvent event) {
        if (getCurrentSong().getLength() - event.getElapsedTime() < END_SONG_DIFF) {
            setReachedEnd(true);
        } else {
            setReachedEnd(false);
        }
    }

    /**
     * @return the reachedEnd
     */
    private boolean isReachedEnd() {
        return reachedEnd;
    }

    /**
     * @param reachedEnd the reachedEnd to set
     */
    private void setReachedEnd(boolean reachedEnd) {
        this.reachedEnd = reachedEnd;
    }

    /**
     * @return the playlistRenderer
     */
    public PlaylistCellRenderer getPlaylistRenderer() {
        return playlistRenderer;
    }

    private class SonglistPopupListener extends MouseAdapter {

        public void mousePressed(MouseEvent evt) {
            if (SwingUtilities.isRightMouseButton(evt)) {
                maybeShowPopup(evt);

            }
        }

        public void mouseReleased(MouseEvent evt) {
            maybeShowPopup(evt);
        }

        private void maybeShowPopup(MouseEvent evt) {
            int row = listPlaylist.locationToIndex(evt.getPoint());

            MPDSong song = (MPDSong) playlistSongsModel.getElementAt(row);

            PlaylistPopupMenu popupMenu =
                    new PlaylistPopupMenu(Utils.getParentComponent(evt.getComponent()),
                    getPlaylist(),
                    getPlayer(),
                    song);

            if (evt.isPopupTrigger()) {
                popupMenu.show(evt.getComponent(),
                        evt.getX(), evt.getY());
            }
        }
    }
}
